use std::cell::Cell;

use egui::{Rect, Rgba};
use nalgebra::{Matrix4, Quaternion, UnitQuaternion, Vector2, Vector3, Vector4};
use thunderdome::Index;
use QcCore::{
    ecs::{component::Component, components::transform::Transform},
    scene_system::scene::Scene,
};
use QcTools::utils::r#ref::Ref;
use QcUI::rect::QcRect;

#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum GizmoOperation {
    Translate,
    Rotate,
    Scale,
}

impl Into<String> for GizmoOperation {
    fn into(self) -> String {
        match self {
            GizmoOperation::Translate => "Translate",
            GizmoOperation::Rotate => "Rotate",
            GizmoOperation::Scale => "Scale",
        }
        .to_string()
    }
}

impl Into<&str> for GizmoOperation {
    fn into(self) -> &'static str {
        match self {
            GizmoOperation::Translate => "Translate",
            GizmoOperation::Rotate => "Rotate",
            GizmoOperation::Scale => "Scale",
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub enum Direction {
    X = 0,
    Y = 1,
    Z = 2,
}

#[derive(Debug)]
pub struct GizmoBehavior {
    pub direction: Option<Direction>,
    pub operation: Option<GizmoOperation>,
    pub target: Option<Index>,
    pub transform: Option<Index>,
    pub original_transform: Option<Transform>,
    pub first_mouse: bool,
    pub origin_mouse: Vector2<f32>,
    pub current_mouse: Vector2<f32>,
    // 选中物体到摄像机的距离
    pub distance: f32,
}

impl GizmoBehavior {
    pub fn new() -> Self {
        let this = Self {
            direction: None,
            target: None,
            transform: None,
            first_mouse: false,
            original_transform: None,
            operation: None,
            origin_mouse: Vector2::identity(),
            current_mouse: Vector2::identity(),
            distance: 0.,
        };
        this
    }

    pub fn get_direction_by_rgba(&self, rgba: &[u8; 4]) -> Option<Direction> {
        if rgba[0] > 250 && rgba[1] == 0 && rgba[2] == 0 {
            Some(Direction::X)
        } else if rgba[1] > 250 && rgba[0] == 0 && rgba[2] == 0 {
            Some(Direction::Y)
        } else if rgba[2] > 250 && rgba[1] == 0 && rgba[0] == 0 {
            Some(Direction::Z)
        } else {
            None
        }
    }

    pub fn is_picking(&self) -> bool {
        self.target.is_some()
    }

    pub fn start_picking(
        &mut self,
        target: Index,
        transform: Index,
        original_transform: Transform,
        distance: f32,
        operation: GizmoOperation,
        direction: Direction,
    ) {
        println!("当前点击的坐标轴：{:?}", direction);
        self.target = Some(target);
        self.transform = Some(transform);
        self.original_transform = Some(original_transform);
        self.first_mouse = true;
        self.distance = distance;
        self.operation = Some(operation);
        self.direction = Some(direction);
    }

    pub fn stop_picking(&mut self) {
        self.target = None;
        self.transform = None;
    }

    pub fn set_current_mouse(&mut self, x: f32, y: f32) {
        if self.first_mouse {
            self.origin_mouse = Vector2::new(x, y);
            self.current_mouse = Vector2::new(x, y);
            self.first_mouse = false;
        } else {
            self.current_mouse = Vector2::new(x, y);
        }
    }

    pub fn apply_operation(
        &mut self,
        scene: &mut Scene,
        view_matrix: Matrix4<f32>,
        proj_matrix: Matrix4<f32>,
        rect: Rect,
    ) {
        if let Some(operation) = self.operation {
            match operation {
                GizmoOperation::Translate => {
                    self.apply_translate(scene, view_matrix, proj_matrix, rect)
                }
                GizmoOperation::Rotate => self.apply_rotate(scene, view_matrix, proj_matrix, rect),
                GizmoOperation::Scale => self.apply_scale(scene, view_matrix, proj_matrix, rect),
            }
        }
    }

    fn apply_translate(
        &mut self,
        scene: &mut Scene,
        view_matrix: Matrix4<f32>,
        proj_matrix: Matrix4<f32>,
        rect: Rect,
    ) {
        let units_per_pixel = 0.002 * self.distance;

        let screen_dir = self.get_screen_direction(scene, view_matrix, proj_matrix, rect);

        let total_offset = self.current_mouse - self.origin_mouse;

        let translation_coefficient = total_offset.dot(&screen_dir) * units_per_pixel;

        if let Component::Transform(transform) =
            &mut scene[self.target.unwrap()][self.transform.unwrap()]
        {
            if let Some(original_transform) = &self.original_transform {
                let position = original_transform.position;

                transform
                    .set_position(position + self.get_real_direction() * translation_coefficient);
            }
        }
    }

    fn apply_rotate(
        &mut self,
        scene: &mut Scene,
        view_matrix: Matrix4<f32>,
        proj_matrix: Matrix4<f32>,
        rect: Rect,
    ) {
        let units_per_pixel = 0.2f32;
        let screen_dir = self.get_screen_direction(scene, view_matrix, proj_matrix, rect);
        let screen_dir = Vector2::new(-screen_dir.y, screen_dir.x);
        let total_offset = self.current_mouse - self.origin_mouse;

        let rotation_coefficient = total_offset.dot(&screen_dir) * units_per_pixel;

        if let Component::Transform(transform) =
            &mut scene[self.target.unwrap()][self.transform.unwrap()]
        {
            if let Some(original_transform) = &self.original_transform {
                let origin_rotation = original_transform.rotation;
                let rotation_to_apply = self.get_fake_direction() * rotation_coefficient;
                let rotation = origin_rotation + rotation_to_apply;
                transform.set_rotation(rotation);
            }
        }
    }

    fn apply_scale(
        &mut self,
        scene: &mut Scene,
        view_matrix: Matrix4<f32>,
        proj_matrix: Matrix4<f32>,
        rect: Rect,
    ) {
        let units_per_pixel = 0.003;

        let screen_dir = self.get_screen_direction(scene, view_matrix, proj_matrix, rect);

        let total_offset = self.current_mouse - self.origin_mouse;

        let scale_coefficient = total_offset.dot(&screen_dir) * units_per_pixel;

        if let Component::Transform(transform) =
            &mut scene[self.target.unwrap()][self.transform.unwrap()]
        {
            if let Some(original_transform) = &self.original_transform {
                let scale = original_transform.scale;
                let mut scale = scale + self.get_fake_direction() * scale_coefficient;
                scale.x = scale.x.max(0.001f32);
                scale.y = scale.y.max(0.001f32);
                scale.z = scale.z.max(0.001f32);
                transform.set_scale(scale);
            }
        }
    }

    fn get_screen_direction(
        &self,
        scene: &mut Scene,
        view_matrix: Matrix4<f32>,
        proj_matrix: Matrix4<f32>,
        rect: Rect,
    ) -> Vector2<f32> {
        if let Some(transform) = &self.original_transform {
            let start = transform.get_world_position(scene);

            let end = start + self.get_real_direction() * 0.01f32;

            let start2d = {
                let clip_space_pos =
                    proj_matrix * view_matrix * Vector4::new(start.x, start.y, start.z, 1.0);
                let ndc_space_pos =
                    Vector3::new(clip_space_pos.x, clip_space_pos.y, clip_space_pos.z)
                        / clip_space_pos.w;
                let mut window_space_pos =
                    Vector2::new(ndc_space_pos.x + 1.0, ndc_space_pos.y + 1.0) / 2.0;
                window_space_pos.x *= rect.width();
                window_space_pos.y *= rect.height();
                window_space_pos
            };

            let end2d = {
                let clip_space_pos =
                    proj_matrix * view_matrix * Vector4::new(end.x, end.y, end.z, 1.0);
                let ndc_space_pos =
                    Vector3::new(clip_space_pos.x, clip_space_pos.y, clip_space_pos.z)
                        / clip_space_pos.w;
                let mut window_space_pos =
                    Vector2::new(ndc_space_pos.x + 1.0, ndc_space_pos.y + 1.0) / 2.0;
                window_space_pos.x *= rect.width();
                window_space_pos.y *= rect.height();
                window_space_pos
            };

            let mut result = end2d - start2d;
            result.y *= -1.;
            return result.normalize();
        }

        panic!("获取Transform组件失败")
    }

    pub fn get_real_direction(&self) -> Vector3<f32> {
        if let Some(direction) = self.direction {
            if let Some(original_transform) = &self.original_transform {
                return match direction {
                    Direction::X => original_transform.get_world_rotation() * Vector3::x(),
                    Direction::Y => original_transform.get_world_rotation() * Vector3::y(),
                    Direction::Z => original_transform.get_world_rotation() * Vector3::z(),
                };
            }
        }

        Vector3::zeros()
    }

    pub fn get_fake_direction(&self) -> Vector3<f32> {
        if let Some(direction) = self.direction {
            return match direction {
                Direction::X => Vector3::x(),
                Direction::Y => Vector3::y(),
                Direction::Z => Vector3::z(),
            };
        }

        Vector3::zeros()
    }
}
